

/***
 * @Date            : 2024-12-24 23:17:10
 * @Author          : AnimateX(Baron)
 * @LastEditors     : AnimateX(Baron)
 * @LastEditTime    : 2024-12-24 23:17:12
 * @FilePath        : /include/logger.h
 * @CopyRight       : Copyright (c) 2024 AnimateX. All rights reserved.
 * @Description     :
 */
#pragma once

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#include "spdlog/common.h"
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/callback_sink.h"
#include <spdlog/fmt/ostr.h>

#include <memory>
#include <functional>
#include <vector>
#include <string>


namespace rc {
    enum class LogLevel {
        LOG_LEVEL_TRACE,    /**< out put all log info       */
        LOG_LEVEL_DEBUG,    /**< just <= debug log info     */
        LOG_LEVEL_INFO,     /**< just <= information        */
        LOG_LEVEL_WARN,     /**< just <= warning            */
        LOG_LEVEL_ERROR,    /**< just <= error              */
        LOG_LEVEL_FATAL,    /**< just <= fatal error        */
        LOG_LEVEL_OFF       /**< off (close logger)         */
    };


    class RCLogger {
    public:
        static RCLogger& getInstance() {
            static RCLogger instance;
            return instance;
        }

        // Prevent copy and assignment.
        RCLogger(const RCLogger&) = delete;
        RCLogger& operator=(const RCLogger&) = delete;

        using LogCallback = std::function<void(const std::string& level, const std::string& message)>;

    private:
        std::shared_ptr<spdlog::logger> logger;
        std::vector<LogCallback> callbacks;

        static spdlog::level::level_enum toSpdLogLevel(LogLevel level) {
            switch (level) {
                case LogLevel::LOG_LEVEL_TRACE:   return spdlog::level::trace;
                case LogLevel::LOG_LEVEL_DEBUG:   return spdlog::level::debug;
                case LogLevel::LOG_LEVEL_INFO:    return spdlog::level::info;
                case LogLevel::LOG_LEVEL_WARN:    return spdlog::level::warn;
                case LogLevel::LOG_LEVEL_ERROR:   return spdlog::level::err;
                case LogLevel::LOG_LEVEL_FATAL:   return spdlog::level::critical;
                case LogLevel::LOG_LEVEL_OFF:     return spdlog::level::off;
                default:                          return spdlog::level::err;
            }
        }

    public:
        RCLogger() {
            const int queueSize = 8192;
            const int threadCount = std::thread::hardware_concurrency();
            spdlog::init_thread_pool(queueSize, threadCount);  // Initialize thread pool for async logging

            auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
            console_sink->set_level(spdlog::level::debug);
            // console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%L%$] [Thread: %t] [%s %!:%#] %v");
            console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%L%$] [Thread: %t] %v");

            auto file_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("./rk_camera_log.txt", 0, 0);
            file_sink->set_level(spdlog::level::debug);
            // file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%L%$] [Thread: %t] [%s %!:%#] %v");
            file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%L%$] [Thread: %t] %v");

            auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>(
                [this](const spdlog::details::log_msg& msg) {
                    // 获取日志级别的字符串表示
                    spdlog::level::level_enum log_level = msg.level;
                    auto level_str = spdlog::level::to_string_view(log_level);

                    // 格式化日志消息
                    std::string formatted_msg = fmt::to_string(msg.payload);

                    // 将 string_view 转换为 std::string
                    std::string level_string(level_str.data(), level_str.size());

                    // 调用所有注册的回调
                    for (auto& callback : callbacks) {
                        callback(level_string, formatted_msg);
                    }
                }
            );

            callback_sink->set_level(spdlog::level::info);

            spdlog::sinks_init_list sink_list = {console_sink, file_sink, callback_sink};

            logger = std::make_shared<spdlog::async_logger>("rc_logger", sink_list.begin(), sink_list.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
            logger->set_level(spdlog::level::debug);
            spdlog::set_default_logger(logger);
        }

        void addCallback(const LogCallback& callback) {
            callbacks.push_back(callback);
        }

        void log(LogLevel level, const std::string& msg) {
            spdlog::level::level_enum spdLevel = toSpdLogLevel(level);
            logger->log(spdLevel, msg);
            for (auto& callback : callbacks) {
                callback(std::to_string(static_cast<int>(level)), msg);
            }
        }

        template<typename... Args>
        void rs_trace(const char* fmt, Args&&... args) {
            logger->trace(fmt, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void rs_debug(const char* fmt, Args&&... args) {
            logger->debug(fmt, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void rs_info(const char* fmt, Args&&... args) {
            logger->info(fmt, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void rs_warn(const char* fmt, Args&&... args) {
            logger->warn(fmt, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void rs_error(const char* fmt, Args&&... args) {
            logger->error(fmt, std::forward<Args>(args)...);
        }

        template<typename... Args>
        void rs_critical(const char* fmt, Args&&... args) {
            logger->critical(fmt, std::forward<Args>(args)...);
        }

        void setLogLevel(LogLevel level) {
            spdlog::level::level_enum spdLevel = toSpdLogLevel(level);
            logger->set_level(spdLevel);
        }

        ~RCLogger() {
            spdlog::shutdown();
        }
    };
}
